

module uart_rx(

    //Receive Data
    rxd,

    //Uart Receive Access Interface
    uart_rx_data,
    uart_rx_vld,
    uart_rx_err,

    //Configuration
    uart_conf,

    clk_sys,
    rst_n

    );

//Receive Data
input rxd;

//Uart Receive Access Interface
output [7:0] uart_rx_data;
output       uart_rx_vld;
output [1:0] uart_rx_err;

//Configuration
input [31:0] uart_conf; 

input clk_sys;
input rst_n;


//Configure Uart Transmit Parameter
wire [3:0] uart_baud_rate = uart_conf[3:0];
//   Conf. Value   Baud Rate
//       0x1       9600
//       0x2       19200
//       0x3       28800
//       0x4       38400
//       0x6       57600
//       0xC       115200
wire parity_check = uart_conf[4];
wire stop_bit     = uart_conf[8];

//System Clock is 100M
reg [15:0] baud_period;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    baud_period <= 16'h0;
else
    begin
    case (uart_baud_rate)
        4'h1:    baud_period <= 16'h28B1; //10416.6667
        4'h2:    baud_period <= 16'h1458; //5208.3333
        4'h3:    baud_period <= 16'hD90;  //3472.2222
        4'h4:    baud_period <= 16'hA2C;  //2604.1667
        4'h6:    baud_period <= 16'h6C8;  //1736.1111
        4'hC:    baud_period <= 16'h365;  //868.5556
        default: baud_period <= 16'h28B1;
    endcase
    end
end


//Receive Data Cache
reg [15:0] rxd_dly;
always @ (posedge clk_sys)
    rxd_dly <= {rxd_dly[14:0],rxd};

reg rxd_val;
always @ (posedge clk_sys)
begin
if (rxd_dly == 16'hFFFF)
    rxd_val <= 1'b1;
else 
    begin
    if (rxd_dly == 16'h0)
        rxd_val <= 1'b0;
    end
end


//Start Uart
reg rxd_val_dly;
always @ (posedge clk_sys)
    rxd_val_dly <= rxd_val;
wire uart_start;
assign uart_start = rxd_val_dly & ~rxd_val;


//Main FSM
reg [2:0] sta_uart;
localparam IDLE   = 3'h0,
           START  = 3'h1,
           WORK   = 3'h2,
           PARITY = 3'h3,
           STOP   = 3'h4,
           DONE   = 3'h5;
           
reg       baud_clk_fall;
reg [3:0] cnt_rxd;
always @ (posedge clk_sys or negedge rst_n)
begin
if (rst_n == 1'b0)
    sta_uart <= IDLE;
else
    begin
    case (sta_uart)
        IDLE:
            if (uart_start == 1'b1) 
                sta_uart <= START;
        START:
            if (baud_clk_fall == 1'b1)
                sta_uart <= WORK;
        WORK:
            if (baud_clk_fall == 1'b1 && cnt_rxd == 4'h7)
                sta_uart <= PARITY;
        PARITY:
            if (parity_check)
                begin
                if (baud_clk_fall == 1'b1)
                    sta_uart <= STOP;
                end
            else
                sta_uart <= STOP;
        STOP:
            if (stop_bit)
                begin
                if (baud_clk_fall == 1'b1)
                    sta_uart <= DONE;
                end            
            else
                sta_uart <= DONE;
        DONE:
            sta_uart <= IDLE;
        default:
            sta_uart <= IDLE;
    endcase
    end
end


//Uart Status
wire uart_work;
assign uart_work = (sta_uart == IDLE) ? 1'b0 : 1'b1;


//Baud Clock
wire [15:0] half_baud_period;
assign half_baud_period = {1'b0,baud_period[15:1]};

reg [15:0] cnt_baud;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    cnt_baud <= 16'h0;
else 
    begin
    if (uart_work == 1'b1)
        begin
        if (cnt_baud >= baud_period)
            cnt_baud <= 16'h0;
        else
            cnt_baud <= cnt_baud + 16'h1;    
        end
    else
        cnt_baud <= 16'h0;
    end
end

//reg baud_clk_fall
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    baud_clk_fall <= 1'b0;
else
    begin
    if (uart_work == 1'b1)
        baud_clk_fall <= (cnt_baud == baud_period) ? 1'b1 : 1'b0;
    end
end
reg baud_clk_rise;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    baud_clk_rise <= 1'b0;
else
    begin
    if (uart_work == 1'b1)
        baud_clk_rise <= (cnt_baud == half_baud_period) ? 1'b1 : 1'b0;
    end
end


//Counter of Receive Data
//reg [3:0] cnt_rxd;
always @ (posedge clk_sys or negedge rst_n)
begin
if (rst_n == 1'b0)
    cnt_rxd <= 4'h0;
else 
    begin
    if (sta_uart == WORK)
        begin
        if (baud_clk_fall == 1'b1)
            cnt_rxd <= cnt_rxd + 4'h1;
        end
    else 
        cnt_rxd <= 4'h0;
    end
end


//Receive Uart Data
reg [8:0] rx_data;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0) 
    rx_data <= 9'h0; 
else
    begin 
    case (sta_uart)
        IDLE:
            rx_data <= 9'h0;
        WORK,PARITY:
            if (baud_clk_rise)
                rx_data <= {rx_data[7:0],rxd_val};          
    endcase 
    end
end


//Output Receive Uart Data
reg [7:0] uart_rx_data;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    uart_rx_data <= 8'h0;
else
    begin
    if (sta_uart == WORK)
        uart_rx_data <= rx_data[7:0];
    end
end

reg uart_rx_vld;
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    uart_rx_vld <= 1'b0;
else
    begin
    if (sta_uart == DONE)
        uart_rx_vld <= (^rx_data[8:1] == rx_data[0]) ? 1'b1 : 1'b0;
    else
        uart_rx_vld <= 1'b0;
    end
end

reg [1:0] uart_rx_err;
//0x1  Parity Check Error
//0x2  Start Bit Error
//0x3  Stop Bit Error
always @ (posedge clk_sys or negedge rst_n) 
begin
if (rst_n == 1'b0)
    uart_rx_err <= 2'h0;
else
    begin
    case (sta_uart)
        START:
            if (baud_clk_rise == 1'b1 && rxd_val == 1'b1)
                uart_rx_err <= 2'h2;
            else
                uart_rx_err <= 2'h0;
        STOP:
            if (baud_clk_rise == 1'b1 && rxd_val == 1'b1)
                uart_rx_err <= 2'h3;
            else
                uart_rx_err <= 2'h0;
        DONE:
            uart_rx_err <= (^rx_data[8:1] == rx_data[0]) ? 2'h0 : 2'h1;
        default:
            uart_rx_err <= 2'h0;
    endcase
    end
end


endmodule
